#pragma once


#include<jsoncpp/json/json.h>
#include"index.hpp"
#include<algorithm>
#include"log.hpp"



namespace ns_searcher{
  
  struct InvertedElemPrint{
  uint64_t doc_id;
  int weight;
  std::vector<std::string>words;
  InvertedElemPrint():doc_id(0),weight(0){}

  };




  class Searcher
  {
    private:
      ns_index::Index * index;
    public:
      Searcher(){}
      ~Searcher(){}
    public:
    //1.初始化Searcher模块，并通过 raw.txt 构建索引。
      void InitSearcher(const std::string &input)
      {
        index=ns_index::Index::GetInstance();
       // std::cout<<"获取index单例对象成功"<<std::endl;
       LOG(NORMAL,"获取index单例对象成功!");
        index->BuildIndex(input);
       // std::cout<<"建立正排索引和倒排索引成功"<<std::endl;
       LOG(NORMAL,"建立正派索引和倒排索引成功!");
      }


      //2.通过搜索关键词进行查询，最终返回json串。
      void Search(const std::string& query_words,std::string*json_string)
      {
        //1.对搜索关键词进行分词
        std::vector<std::string> words;
        ns_util::JiebaUtil::CutString(query_words,&words);
        //list用于存储所有关键词对应的倒排拉链中的所有的倒排索引结构体对象
        std::unordered_map<uint64_t,InvertedElemPrint> unique_map;
        // std::vector<ns_index::InvertedElem> list;
        //2.对分词之后的关键词，查询各个关键词的倒排拉链
        for( std::string word:words)
        {
          //对查询的关键词分词之后的关键词在进行查询时也是不区分大小写的
          boost::to_lower(word);
          std::vector<ns_index::InvertedElem>* lt= index->GetInvertedList(word);  
          if(nullptr==lt)
          {
           // std::cout<<"get inverted list error"<<std::endl;
           LOG(WARNING,"get inverted list error!");
            continue;
          }
          for(auto& item:(*lt))
          {
               InvertedElemPrint& ip= unique_map[item.doc_id];
               ip.doc_id=item.doc_id;
               ip.weight+=item.weight;
               ip.words.push_back(std::move(item.word));
               
          }
        }
        
              std::vector<InvertedElemPrint> list;
        for(auto& item:unique_map)
        {
              list.push_back(std::move(item.second));
        }
          //成功获取到了关键词对应的倒排拉链
          //将获取的倒排拉链的每个元素插入到list中
          //list.insert(list.end(),lt->begin(),lt->end());
          
          //对list中的倒排索引结构体对象进行排序，以weight大小的方式进行降序排序
          //std::sort(list.begin(),list.end(),[](const ns_index::InvertedElem& em1,const ns_index::InvertedElem& em2){ return em1.weight>em2.weight;  });
          std::sort(list.begin(),list.end(),[](const InvertedElemPrint& im1,const InvertedElemPrint& im2){return im1.weight>im2.weight;}); 
          //依次根据排序之后的倒排索引对象获取该对象内部的id对应的正排索引对象
          Json::Value root;
          for(auto& em:list)
          {
          ns_index::DocInfo* doc = index->GetForwardIndex(em.doc_id);
          //获取到对应的正派索引结构体对象之后，将对象中的 title,content,url 转成json串并返回
          if(nullptr==doc)
          {
           // std::cerr<<"get forward index error"<<std::endl;
              LOG(WARNING,"get forward index error!");
            continue;
          }
          Json::Value elem;
          elem["title"]=doc->title;
          elem["desc"]=GetDesc(doc->content,em.words[0]);
          elem["url"]=doc->url;
          elem["id"]=(int)doc->doc_id;
          elem["weight"]=em.weight;
          root.append(elem);
          }
          //使用writer对象对root对象中的结构化数据进行序列化 
          //Json::StyledWriter writer;
          Json::FastWriter writer;
          *json_string=writer.write(root);
      }
        std::string GetDesc(const std::string& content,const std::string& word)
        {
            //在DocInfo对象的content中找到InvertedElem中word首次出现的位置
            
          auto iter= std::search(content.begin(),content.end(),word.begin(),word.end(),[](const char &x,const char &y){return std::tolower(x)==std::tolower(y);});
          if(iter==content.end())
           {
             return "none1";
           }
            int  pos =iter-content.begin();
            int begin=0;
            int end=content.size()-1;
            int step_front =50;
            int  step_back=100;
           if(pos-step_front>begin) begin=pos-step_front;
           if(pos+step_back<end) end=pos+step_back;
           if(begin<end)
           { 
             std::string desc= content.substr(begin,end-begin);
             desc+="......";
             return desc;
           }
           return "none2";
        }
  };
























}
